#ifndef SUITE_HPP__
# define SUITE_HPP__

# include <list>
# include <string>
# include <chrono>

# include "TestInfo.hpp"
# include "ConsoleOutput.hpp"

namespace Test
{
    template <typename T, class Output = ConsoleOutput>
    class Suite : public Output
    {
    public:
        typedef void (T::*testFunction_t)();

    protected:
        std::string _name;

    private:
        typedef std::chrono::time_point<std::chrono::system_clock> TimePoint;

    private:
        std::list<std::pair<TestInfo, testFunction_t> > _test_suite;
        static std::list<std::string> _failure_msgs;

    public:
        Suite() : _name("unnamed") {}
        virtual ~Suite() {}

        void addTestFunction(testFunction_t tf, std::string const & name)
        {
            this->_test_suite.push_back(std::pair<TestInfo, testFunction_t>(TestInfo(name), tf));
        }

        void assert_test(bool expr, std::string const & failureMsg)
        {
            if (!expr)
                this->_failure_msgs.push_back(failureMsg);
        }

        void run() 
        {
            this->print_before_suite(this->_name);
            T instance;
            typename std::list<std::pair<TestInfo, testFunction_t> >::iterator it = this->_test_suite.begin();
            typename std::list<std::pair<TestInfo, testFunction_t> >::iterator ite = this->_test_suite.end();

            while (it != ite)
            {
                this->print_before_test(it->first);
                TimePoint testStart = std::chrono::system_clock::now();
                (instance.*(it->second))();
                TimePoint testEnd = std::chrono::system_clock::now();
                std::chrono::microseconds ns = testEnd - testStart;
                it->first.execution_time = ns.count();
                if (!this->_failure_msgs.empty())
                {
                    it->first.is_successful = false;
                    this->print_failure(this->_failure_msgs, it->first);
                    this->_failure_msgs.clear();
                }
                this->print_after_test(it->first);
                ++it;
            }
            this->print_after_suite(this->_name, this->getTestInfoList());
        }

    private:
        std::list<TestInfo>* getTestInfoList()
        {
            std::list<TestInfo>* list = new std::list<TestInfo>;
            typename std::list<std::pair<TestInfo, testFunction_t> >::iterator it = this->_test_suite.begin();
            typename std::list<std::pair<TestInfo, testFunction_t> >::iterator ite = this->_test_suite.end();

            while (it != ite)
            {
                list->push_back(it->first);
                ++it;
            }
            return list;
        }
    };

    template<typename T, class Output> std::list<std::string> Suite<T, Output>::_failure_msgs;
}



#endif // SUITE_HPP__

